/**
 * 
 */
package edu.umd.clip.lm.ngram;

import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
import java.lang.management.MemoryUsage;
import java.util.Arrays;

import edu.berkeley.nlp.util.Option;
import edu.berkeley.nlp.util.OptionParser;


public abstract class Ngram {
	long future;
	
	public Ngram(long future) {
		this.future = future;
	}

	public static Ngram makeNgram(int order, long[] context, long future) {
		if (order == 1) {
			return new Unigram(future);
		}
		
		int first = context.length - order + 1;
		if (order == 7) {
			return new Sevengram(context[first], context[first+1], context[first+2], context[first+3], context[first+4], context[first+5], future);
		} else if (order == 6) {
			return new Sixgram(context[first], context[first+1], context[first+2], context[first+3], context[first+4], future);
		} else if (order == 5) {
			return new Fivegram(context[first], context[first+1], context[first+2], context[first+3], future);
		} else if (order == 4) {
			return new Fourgram(context[first], context[first+1], context[first+2], future);
		} else if (order == 3) {
			return new Trigram(context[first], context[first+1], future);
		} else if (order == 2) {
			return new Bigram(context[first], future);
		}
		throw new Error("Bad context lengths: " + context.length);
	}
	
	public static Ngram makeNgram(long[] context) {
		return makeNgram(Arrays.copyOf(context, context.length-1), context[context.length-1]);
	}
	
	public static Ngram makeNgram(long[] context, long future) {
		if (context == null || context.length == 0) {
			return new Unigram(future);
		} else if (context.length == 3) {
			return new Fourgram(context, future);
		} else if (context.length == 2) {
			return new Trigram(context, future);
		} else if (context.length == 1) {
			return new Bigram(context, future);
		} 
		throw new Error("Bad context lengths: " + context.length);
	}
	
	public abstract int getOrder();
	public abstract long[] getContext();
	public abstract Ngram getLowerOrderNgram();
	
	public long getFuture() {
		return future;
	}
	
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + (int) (future ^ (future >>> 32));
		return result;
	}
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (!(obj instanceof Ngram))
			return false;
		Ngram other = (Ngram) obj;
		if (future != other.future)
			return false;
		return true;
	}
	
	public static class Options {
        @Option(name = "-order", required = true, usage = "n-gram order to test")
		public int order;
        @Option(name = "-wrapper", usage = "test the array wrapper (Default: n-gram)")
		public boolean wrapper = false;
	}
	
	public static void main(String[] args) {
		final int NUM_NGRAMS = 10000000;
		
		class NgramWrapper {
			@SuppressWarnings("unused")
			public long ngram[];
			public NgramWrapper(int order) {
				this.ngram = new long[order];
			}
		}

		OptionParser optParser = new OptionParser(Options.class);
        final Options opts = (Options) optParser.parse(args, true);

        final MemoryMXBean memoryBean = ManagementFactory.getMemoryMXBean();

        MemoryUsage memuse1, memuse2;
        System.gc();
        NgramWrapper[] a = null;
        Ngram[] b = null;
        
        if (opts.wrapper) {
			a = new NgramWrapper[NUM_NGRAMS];

			memuse1 = memoryBean.getHeapMemoryUsage();
			for(int i=0; i<NUM_NGRAMS; ++i) {
				a[i] = new NgramWrapper(opts.order);
			}
			System.gc();
			memuse2 = memoryBean.getHeapMemoryUsage();
        } else {
			b = new Ngram[NUM_NGRAMS];

			memuse1 = memoryBean.getHeapMemoryUsage();
			for(int i=0; i<NUM_NGRAMS; ++i) {
				b[i] = Ngram.makeNgram(opts.order, new long[opts.order-1], 0);
			}
			System.gc();
			memuse2 = memoryBean.getHeapMemoryUsage();
        }
			
        System.out.printf("Order %d: %s: %d bytes/ngram\n", 
        		opts.order, opts.wrapper ? "wrapper" : "n-gram", (memuse2.getUsed() - memuse1.getUsed())/NUM_NGRAMS);
		int l = (a == null) ? b.length : a.length;
	}
}